Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

States and projections #1

Merged
merged 2 commits into from
Jan 5, 2024
Merged

States and projections #1

merged 2 commits into from
Jan 5, 2024

Conversation

Almaju
Copy link
Contributor

@Almaju Almaju commented Jan 5, 2024

Général :

  • Par convention, tous les noms de fichiers sont au singulier car ce sont des modules (comme ça plus de doute/inconsistence)
  • Fix d'une typo dans le Justfile
  • Ajout de --nocapture dans just test pour pouvoir voir les println

Domain:

  • Déplacement de la logique send et apply dans le State puisque sinon ça serait contradictoire avec la logique des Projection
  • Renommage de todolist.rs en todolist_state.rs pour plus de clarté
  • Rajout de newtypes TaskName et TaskIndex pour plus de clarté et valider que la string est non-vide
  • HashMap plutôt que Vec pour mieux gérer les index + erreurs quand l'index n'existe pas

Application :

  • Refacto de Projection State -> Projection => Projection + Event[] -> Projection
  • Fusion des commandes et du pull_and_push via un enum Command
  • Renommage des traits Command et Query en un seul trait Execute (Effect aurait aussi pu être utilisé ?)
  • Conversion de Command -> Result<Message, CommandError>

Client Axum :

  • Erreur HTTP 400 dans le cas d'une erreur
  • Plus besoin de generics dans le handle_command grâce à l'enum
  • Un seul endpoint /todolist avec GET et POST pour tout gérer
  • Rajout d'exemples de calls HTTP

Next? :

  • Utiliser un enum pour Query et Projection comme pour Command ?
  • Renommer State -> Apply + Send et Projection -> Apply pour éviter les collisions et avoir plus de flexibilité ?
  • Ajouter du tracing ?

@Almaju Almaju force-pushed the states-and-projections branch from 4c56f37 to fa3ec8a Compare January 5, 2024 16:47
@Almaju Almaju merged commit 1c309a4 into main Jan 5, 2024
1 of 4 checks passed
@Almaju Almaju deleted the states-and-projections branch January 5, 2024 18:05
@Almaju
Copy link
Contributor Author

Almaju commented Jan 5, 2024

@CodiumAI-Agent /review

@CodiumAI-Agent
Copy link

PR Analysis

  • 🎯 Main theme: Refactoring and enhancing the TodoList application
  • 📝 PR summary: This PR introduces significant changes to the TodoList application, including renaming files for clarity, moving logic around for better organization, and introducing new types for better code readability and error handling. It also includes changes to the way commands and queries are handled, and how HTTP calls are made in the client.
  • 📌 Type of PR: Enhancement
  • 🧪 Relevant tests added: Yes
  • ⏱️ Estimated effort to review [1-5]: 4, because the PR introduces significant changes across multiple files, including logic changes, renaming, and reorganization. It requires a good understanding of the application to review effectively.
  • 🔒 Security concerns: No security concerns found

PR Feedback

💡 General suggestions: The PR is well-structured and introduces several improvements to the codebase. The use of newtypes for task names and indices improves code readability and error handling. The changes to the command and query handling also make the code more organized and easier to follow. However, it would be beneficial to provide more context and explanation in the PR description for such a large set of changes. This would help reviewers understand the rationale behind each change and how they all fit together.

🤖 Code feedback:
relevant filedomain/src/todolist_state.rs
suggestion      

Consider using more descriptive enum variants for the Completed enum. Instead of Yes and No, you could use Completed and NotCompleted for better readability. [medium]

relevant linepub enum Completed {

relevant filedomain/src/todolist_state.rs
suggestion      

The send method could be refactored to avoid nested match statements, which can be hard to read. Consider using if let or map for the inner match. [medium]

relevant linefn send(&self, message: &Self::Message) -> Result<VecSelf::Event, Self::Error> {

relevant fileclients/yew/src/main.rs
suggestion      

Consider using constants for the HTTP endpoints instead of hardcoding them. This would make it easier to manage and update them. [medium]

relevant line.route("/todolist", get(get_todolist))

relevant fileclients/axum/src/main.rs
suggestion      

Consider handling the error case where the command execution fails more gracefully. Currently, the error is simply logged to the console. You could return a meaningful error response to the client. [important]

relevant linelet res = Command::AddTask { name }.execute(runtime.as_ref()).await;

✨ Usage tips:

Tag me in a comment '@CodiumAI-Agent' and add one of the following commands:

  • /review: Request a review of your Pull Request.
  • /describe: Update the PR title and description based on the contents of the PR.
  • /improve [--extended]: Suggest code improvements. Extended mode provides a higher quality feedback.
  • /ask <QUESTION>: Ask a question about the PR.
  • /update_changelog: Update the changelog based on the PR's contents.
  • /add_docs 💎: Generate docstring for new components introduced in the PR.
  • /generate_labels 💎: Generate labels for the PR based on the PR's contents.
  • /analyze 💎: Automatically analyzes the PR, and presents changes walkthrough for each component.

See the tools guide for more details.
To edit any configuration parameter from the configuration.toml, add --config_path=new_value.
For example: /review --pr_reviewer.extra_instructions="focus on the file: ..."
To list the possible configuration parameters, add a /config comment.

@Almaju
Copy link
Contributor Author

Almaju commented Jan 5, 2024

@CodiumAI-Agent /improve --extended

@@ -0,0 +1,55 @@
use crate::port::{TodoListRepository, TodoListStore};
use domain::todolist_message::TodoListMessage;
use framework::*;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider using a more specific import for the framework::* module instead of importing all its contents. [best practice]

Suggested change
use framework::*;
use framework::{AnyResult, Error};

pub use crate::projections::TodoListProjection;
pub use domain::{todolist::TodoList, todolist_event::TodoListEvent};
pub use crate::projection::TodoListProjection;
pub use domain::{todolist_event::TodoListEvent, todolist_state::TodoList};
use framework::*;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider using a more specific import for the framework::* module instead of importing all its contents. [best practice]

Suggested change
use framework::*;
use framework::{AnyResult, Error};

Comment on lines +7 to +11
pub enum Command {
AddTask { name: String },
RemoveTask { index: usize },
CompleteTask { index: usize },
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding a docstring to the Command enum. [best practice]

Suggested change
pub enum Command {
AddTask { name: String },
RemoveTask { index: usize },
CompleteTask { index: usize },
}
#[derive(Deserialize)]
/// Represents a command to be executed on the todo list.
pub enum Command {

Comment on lines +13 to +17
#[derive(Debug, Error)]
pub enum CommandError {
#[error("Task name cannot be empty")]
TaskNameCannotBeEmpty,
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding a docstring to the CommandError enum. [best practice]

Suggested change
#[derive(Debug, Error)]
pub enum CommandError {
#[error("Task name cannot be empty")]
TaskNameCannotBeEmpty,
}
#[derive(Debug, Error)]
/// Represents an error that can occur while executing a command on the todo list.
pub enum CommandError {

Comment on lines +19 to +33
impl TryInto<TodoListMessage> for &Command {
type Error = CommandError;

fn try_into(self) -> Result<TodoListMessage, Self::Error> {
Ok(match self {
Command::AddTask { name } => TodoListMessage::AddTask(
name.clone()
.try_into()
.map_err(|_| CommandError::TaskNameCannotBeEmpty)?,
),
Command::RemoveTask { index } => TodoListMessage::RemoveTask((*index).into()),
Command::CompleteTask { index } => TodoListMessage::CompleteTask((*index).into()),
})
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding a docstring to the try_into function. [best practice]

Suggested change
impl TryInto<TodoListMessage> for &Command {
type Error = CommandError;
fn try_into(self) -> Result<TodoListMessage, Self::Error> {
Ok(match self {
Command::AddTask { name } => TodoListMessage::AddTask(
name.clone()
.try_into()
.map_err(|_| CommandError::TaskNameCannotBeEmpty)?,
),
Command::RemoveTask { index } => TodoListMessage::RemoveTask((*index).into()),
Command::CompleteTask { index } => TodoListMessage::CompleteTask((*index).into()),
})
}
}
impl TryInto<TodoListMessage> for &Command {
/// Tries to convert the command into a `TodoListMessage`.
/// Returns an error if the conversion fails.
type Error = CommandError;
fn try_into(self) -> Result<TodoListMessage, Self::Error> {
...

Comment on lines +36 to +54
impl<R> Execute<R> for Command
where
R: TodoListRepository + TodoListStore + Send + Sync,
{
async fn execute(&self, runtime: &R) -> AnyResult<()> {
let message = self.try_into()?;

// Pull the current state and apply the message
let todolist = TodoListStore::pull(runtime).await?;
let new_events = todolist.send(&message)?;
TodoListStore::push(runtime, &new_events).await?;

// Save the projection
let mut projection = TodoListRepository::fetch(runtime).await?;
projection.apply(&new_events);
TodoListRepository::save(runtime, &projection).await?;

Ok(())
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding a docstring to the execute function. [best practice]

Suggested change
impl<R> Execute<R> for Command
where
R: TodoListRepository + TodoListStore + Send + Sync,
{
async fn execute(&self, runtime: &R) -> AnyResult<()> {
let message = self.try_into()?;
// Pull the current state and apply the message
let todolist = TodoListStore::pull(runtime).await?;
let new_events = todolist.send(&message)?;
TodoListStore::push(runtime, &new_events).await?;
// Save the projection
let mut projection = TodoListRepository::fetch(runtime).await?;
projection.apply(&new_events);
TodoListRepository::save(runtime, &projection).await?;
Ok(())
}
#[async_trait]
impl<R> Execute<R> for Command
where
R: TodoListRepository + TodoListStore + Send + Sync,
{
/// Executes the command on the todo list.
/// Returns an error if the execution fails.
async fn execute(&self, runtime: &R) -> AnyResult<()> {
...

Comment on lines +6 to +10
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct TodoListProjection {
pub in_progress: HashMap<usize, String>,
pub completed: HashMap<usize, String>,
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding a docstring to the TodoListProjection struct. [best practice]

Suggested change
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct TodoListProjection {
pub in_progress: HashMap<usize, String>,
pub completed: HashMap<usize, String>,
}
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
/// Represents the projection of the todo list.
pub struct TodoListProjection {
pub in_progress: HashMap<usize, String>,
pub completed: HashMap<usize, String>,
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants